package jdmx.framework.common.serialization;

import java.io.IOException;
import java.io.StringReader;
import java.util.List;
import java.util.Set;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;

import jdmx.framework.common.Envelope;
import jdmx.framework.common.envelopes.EnvelopeTyped;
import jdmx.framework.common.envelopes.EnvelopeUntyped;
import jdmx.library.helpers.CompressionHelper;
import jdmx.library.helpers.TypeHelper;

public class Serializer {
	public static Element serializeToXml(Envelope envelope) {

		Element rootElement = new Element("Envelope");
		if (envelope.handlingCode != null) {		
			Element xmlHandlerCode = new Element("handlingCode");
			xmlHandlerCode.setText(envelope.handlingCode);
			rootElement.addContent(xmlHandlerCode);
		}
		if (envelope.methodName != null) {		
			Element xmlMethodName = new Element("methodName");
			xmlMethodName.setText(envelope.methodName);
			rootElement.addContent(xmlMethodName);
		}
		if (envelope.envelopeVersion != null) {		
			Element xmlEnvelopeVersion = new Element("EnvelopeVersion");
			xmlEnvelopeVersion.setText(envelope.envelopeVersion);
			rootElement.addContent(xmlEnvelopeVersion);
		}
		
		XmlSerializerFactory factory = new XmlSerializerFactory();
		Set<String> keySet = envelope.getItemKeySet();
		for (String key : keySet) {
			Object item = envelope.getItem(key);
			String itemType = TypeHelper.getObjectClassName(item);
			XmlSerializer xmlSerializer = factory.create(itemType);
			xmlSerializer.serialize(item, key);
		}
		return rootElement;
	}
	
	public static Envelope deserializeFromXml(Element element)  {
		if (element == null) 
			return null;
		String envelopeType = element.getChild("envelopeType").getText();
		if (envelopeType == null)
			return null;
		Envelope envelope = null;
		if (envelopeType.equalsIgnoreCase("typed")) {
			envelope = new EnvelopeTyped();
		} else if (envelopeType.equalsIgnoreCase("untyped")) {
			envelope = new EnvelopeUntyped();
		} else {
			return null;
		}
		
		if (element.getChild("handlingCode") != null) 
			envelope.handlingCode = element.getChild("handlingCode").getText();
		if (element.getChild("methodName") != null) 
			envelope.methodName = element.getChild("methodName").getText();
		if (element.getChild("envelopeVersion") != null) 
			envelope.envelopeVersion = element.getChild("envelopeVersion").getText();
		if (element.getChild("envelopeType") != null) 
			envelope.envelopeType = element.getChild("envelopeType").getText();

		@SuppressWarnings("unchecked")
		List<Element> xmlChildList = element.getChildren();
		XmlSerializerFactory factory = new XmlSerializerFactory();
		for (Element xmlChild : xmlChildList) {
			String itemName = xmlChild.getName();
			String itemType = xmlChild.getAttributeValue("type");
			XmlSerializer xmlSerializer = factory.create(itemType);
			Object envelopeItem = xmlSerializer.deserialize(xmlChild);
			envelope.setItem(itemName, envelopeItem);
		}
		
		return envelope;
	}
	
	public static String serializeToString(Envelope envelope) {
		Element rootElement = serializeToXml(envelope);
		Document xmlDocument = new Document(rootElement);
	    XMLOutputter outputter = new XMLOutputter();
		String xmlString = outputter.outputString(xmlDocument);
		return xmlString;		
	}

	public static byte[] serializeToByteArray(Envelope envelope) {
		String decompressedData = serializeToString(envelope);
		byte[] compressedData = CompressionHelper.compress(decompressedData);
		return compressedData;
	}
	
	public static Envelope deserializeFromString(String xmlString) {
		SAXBuilder sb = new SAXBuilder();
		Document xmlDocument = null;
		try {
			xmlDocument = sb.build(new StringReader(xmlString));
		} catch (JDOMException e) {
		} catch (IOException e) {
		}

		Envelope dataEnvelope = null; 
		if (xmlDocument != null) {
			Element rootElement = xmlDocument.getRootElement();
			dataEnvelope = deserializeFromXml(rootElement); 
		}
		
		return dataEnvelope;
	}
	
	public static Envelope deserializeFromByteArray(byte[] compressedData) {
		String decompressedData = CompressionHelper.decompress(compressedData);
		Envelope dataEnvelope =  deserializeFromString(decompressedData);
		return dataEnvelope;
	}

	
}
